import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { CreateBlogDto } from './dto/create-blog.dto';
import { UpdateBlogDto } from './dto/update-blog.dto';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { Blog } from './entities/blog.entity';
import {
  EntityManager,
  FindOptionsWhere,
  IsNull,
  LessThan,
  MoreThan,
  Not,
  Repository,
  Like,
} from 'typeorm';
import { CommentService } from '../comment/comment.service';
import { filterSensitiveWords } from '../utils';
import { Comment } from 'src/comment/entities/comment.entity';
import { Like as Likes } from 'src/like/entities/like.entity';
import { UploadService } from 'src/upload/upload.service';
import { Star } from 'src/star/entities/star.entity';
import { QueryBlogDto } from './dto/query-blog.dto';

@Injectable()
export class BlogService {
  @InjectRepository(Blog)
  private blogRepository: Repository<Blog>;

  @InjectEntityManager()
  private manage: EntityManager;

  @Inject(UploadService)
  private uploadService: UploadService;

  @Inject(CommentService)
  private commentService: CommentService;

  async createBlog(createBlogDto: CreateBlogDto) {
    const {
      article_content,
      article_cover,
      article_description,
      article_title,
      authorName,
      userId,
      categoryName,
      category_id,
      type,
      tagIdList,
      tagNameList,
      status,
      is_top,
    } = createBlogDto;
    const filterContent = filterSensitiveWords(article_content);
    const blog = new Blog();
    blog.article_content = filterContent;
    blog.article_cover = article_cover;
    blog.article_description = article_description;
    blog.article_title = article_title;
    blog.authorName = authorName;
    blog.userId = userId;
    blog.categoryName = categoryName;
    blog.category_id = category_id;
    blog.type = type;
    blog.status = status;
    blog.is_top = is_top;
    blog.tagIdList = tagIdList.join(',');
    blog.tagNameList = tagNameList.join(',');
    try {
      await this.blogRepository.save(blog);
      return '创建成功';
    } catch (error) {
      return '创建失败';
    }
  }

  async updateBlog(updateBlogDto: UpdateBlogDto) {
    const {
      id,
      article_content,
      article_cover,
      article_description,
      article_title,
      categoryName,
      category_id,
      tagIdList,
      tagNameList,
      type,
      status,
      is_top,
    } = updateBlogDto;
    const blog: any = await this.getBlogById({ id: id });
    if (!blog) {
      return '找不到文章记录';
    }
    blog.article_content = article_content;
    blog.article_cover = article_cover;
    blog.article_description = article_description;
    blog.article_title = article_title;
    blog.categoryName = categoryName;
    blog.category_id = category_id;
    blog.tagNameList = tagNameList.join(',');
    blog.tagIdList = tagIdList.join(',');
    blog.status = status;
    blog.is_top = is_top;
    blog.type = type;
    try {
      await this.blogRepository.save(blog);
      return '创建成功';
    } catch (error) {
      console.log(error);
      return '创建失败';
    }
  }

  async getBlogById(param: { id: number; userId?: number }) {
    const blog = await this.blogRepository.findOne({
      where: {
        id: Number(param.id),
      },
      relations: ['stars', 'likes'],
    });
    if (!blog) {
      return '没有这篇文章';
    }

    const { data } = await this.commentService.getByCreateId({
      createId: param.id,
      type: 1,
      userId: param.userId,
    });
    return {
      ...blog,
      comments: data,
    };
  }

  async deleteBlogById(id: number) {
    const _Type = 1; //博客的业务类型为1
    // 开启事务
    /**
     * @流程1 博客--->博客点赞--->博客图片@oss --->说图片@upload
     * @流程2 (博客评论--->博客评论图片@oss ---> 博客评论图片@upload --->评论点赞)
     */
    const queryRunner =
      this.blogRepository.manager.connection.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const blog = await this.manage.findOneBy(Blog, { id: id });
      const whereOptions: FindOptionsWhere<Comment> = {
        // name: In(nameValues),
        createId: id,
        type: _Type, //博客的业务类型为1
        commentPic: Not(IsNull()),
      };
      const commentPics = await queryRunner.manager.find(Comment, {
        where: whereOptions,
        select: ['commentPic'],
      });

      // 评论里面有点赞的
      const commentLikeIds = await queryRunner.manager
        .createQueryBuilder(Comment, 'c')
        .select('c.id')
        .where('c.createId = :createId', { createId: id })
        .andWhere('c.type = :type', { type: 2 }) //博客的业务类型为2
        .andWhere('c.like > :condition', { condition: 0 })
        .getMany();

      if (commentLikeIds?.length) {
        //删除评论的点赞
        await queryRunner.manager
          .createQueryBuilder()
          .delete()
          .from(Likes)
          .where('createId IN (:...createIds)', {
            createIds: commentLikeIds.map((i) => i.id),
          })
          .andWhere('type = :type', { type: 3 }) //评论的业务类型3
          .execute();
      }
      // 删除评论
      await queryRunner.manager
        .createQueryBuilder()
        .delete()
        .from(Comment)
        .where('createId = :createId', { createId: id })
        .andWhere('type = :type', { type: _Type })
        .execute();
      // 删除收藏
      await queryRunner.manager
        .createQueryBuilder()
        .delete()
        .from(Star)
        .where('createId = :createId', { createId: id })
        .andWhere('type = :type', { type: _Type })
        .execute();
      // 删除博客点赞
      await queryRunner.manager
        .createQueryBuilder()
        .delete()
        .from(Likes)
        .where('createId = :createId', { createId: id })
        .andWhere('type = :type', { type: _Type })
        .execute();
      const res = await queryRunner.manager.delete(Blog, { id });
      if (res?.affected == 1) {
        let imageUrls = '';
        blog?.article_cover ? (imageUrls += blog.article_cover) : '';
        if (commentPics?.length) {
          imageUrls += ',' + commentPics.map((i) => i.commentPic).join(',');
        }
        if (imageUrls) {
          const urlRes = await this.uploadService.multiDelete(imageUrls);
          console.log('OSS删除成功的图片url', urlRes);
          await this.uploadService.deleteImageUrl(imageUrls);
        }
      } else {
        await queryRunner.rollbackTransaction();
        throw new HttpException('删除出错了', HttpStatus.BAD_REQUEST);
      }
      await queryRunner.commitTransaction();
      return res;
    } catch (error) {
      await queryRunner.rollbackTransaction();
      throw new HttpException('出错回滚', HttpStatus.BAD_REQUEST);
    } finally {
      await queryRunner.release();
    }
  }

  async queryBlogListByPage(page: number, size: number, status?: number) {
    const skip = (page - 1) * size;
    const [blog, total] = await this.blogRepository.findAndCount({
      select: [
        'id',
        'article_content',
        'article_title',
        'createDate',
        'updateDate',
        'article_description',
        'article_cover',
        'authorName',
        'is_top',
        'tagNameList',
        'categoryName',
        'thumbs_up_times',
        'star_times',
        'comment_times',
        'userId',
      ],
      skip: skip,
      take: size,
      where: {
        status: status,
      }
    });
    return {
      blog: blog,
      total: total,
    };
  }

  // 点赞 redis方案
  // async like() {
  //   this.redisService.set('x', 5);
  // }
  // 收藏
  async star(id: number) {
    try {
      await this.blogRepository.increment({ id: id }, 'star_times', 1);
      return '收藏成功';
    } catch (error) {
      new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }
  // 取消收藏
  async unstar(id: number) {
    try {
      await this.blogRepository.decrement({ id: id }, 'star_times', 1);
      return '取消收藏成功';
    } catch (error) {
      new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }

  // 点赞
  async like(id: number) {
    try {
      await this.blogRepository.increment({ id: id }, 'thumbs_up_times', 1);
      return '点赞成功';
    } catch (error) {
      new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }
  // 取消点赞
  async unlike(id: number) {
    try {
      await this.blogRepository.decrement({ id: id }, 'thumbs_up_times', 1);
      return '取消点赞成功';
    } catch (error) {
      new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }

  // 阅读量
  async read(id: number) {
    try {
      await this.blogRepository.increment({ id: id }, 'view_times', 1);
      return 1;
    } catch (error) {
      new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }

  // comment
  async commentIncrement(createId: number) {
    await this.blogRepository.increment({ id: createId }, 'comment_times', 1);
  }

  // delete comment
  async commentDecrement(createId: number) {
    await this.blogRepository.decrement({ id: createId }, 'comment_times', 1);
  }

  async getRelationArticle(id: number) {
    try {
      const preBlog = await this.blogRepository.find({
        select: ['id', 'article_title', 'article_cover'],
        where: { id: LessThan(id) },
        order: { id: 'DESC' },
        take: 1,
      });

      const nextBlog = await this.blogRepository.find({
        select: ['id', 'article_title', 'article_cover'],
        where: { id: MoreThan(id) },
        order: { id: 'ASC' },
        take: 1,
      });

      const recommendList = await this.blogRepository.find({
        select: ['id', 'article_title', 'article_cover', 'createDate'],
        where: { id: Not(id) },
        order: { thumbs_up_times: 'DESC' },
        take: 6,
      });

      return {
        preBlog,
        nextBlog,
        recommendList,
      };
    } catch (error) {
      throw new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }

  async titleExist(title: string) {
    try {
      const res = await this.blogRepository.findOne({
        where: {
          article_title: title,
        },
      });
      if (res) {
        return 1;
      } else {
        return 0;
      }
    } catch (error) {
      throw new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }

  // 收藏的文章列表
  async queryStarBlogList(page: number, size: number, userId: number) {
    const skip = (page - 1) * size;
    const [blog, total] = await this.blogRepository.findAndCount({
      select: [
        'id',
        'article_content',
        'article_title',
        'createDate',
        'updateDate',
        'article_description',
        'article_cover',
        'authorName',
        'is_top',
        'tagNameList',
        'categoryName',
        'thumbs_up_times',
        'star_times',
        'comment_times',
        'userId',
      ],
      where: {
        stars: {
          userId: userId,
        },
      },
      skip: skip,
      take: size,
    });
    return {
      blog: blog,
      total: total,
    };
  }
  // 点赞的文章列表
  async queryLikeBlogList(page: number, size: number, userId: number) {
    const skip = (page - 1) * size;
    const [blog, total] = await this.blogRepository.findAndCount({
      select: [
        'id',
        'article_content',
        'article_title',
        'createDate',
        'updateDate',
        'article_description',
        'article_cover',
        'authorName',
        'is_top',
        'status',
        'tagNameList',
        'categoryName',
        'thumbs_up_times',
        'star_times',
        'comment_times',
      ],
      where: {
        likes: {
          userId: userId,
        },
      },
      skip: skip,
      take: size,
    });
    return {
      blog: blog,
      total: total,
    };
  }
  async queryBlogListByParam(
    page: number,
    size: number,
    queryBlogDto: QueryBlogDto,
  ) {
    const whereOptions: FindOptionsWhere<Blog> = {};
    for (const key in queryBlogDto) {
      if (queryBlogDto[key] !== undefined) {
        whereOptions[key] = queryBlogDto[key];
      }
    }
    const options = {
      where: whereOptions,
    };
    if (page && size) {
      options['skip'] = (page - 1) * size;
      options['take'] = size;
    }
    options['select'] = [
      'id',
      'article_content',
      'article_title',
      'createDate',
      'updateDate',
      'article_description',
      'article_cover',
      'authorName',
      'is_top',
      'status',
      'tagNameList',
      'categoryName',
      'thumbs_up_times',
      'star_times',
      'comment_times',
    ];
    try {
      const [blog, total] = await this.blogRepository.findAndCount(options);
      return {
        blog: blog,
        total: total,
      };
    } catch (error) {
      throw new HttpException(error, HttpStatus.BAD_REQUEST);
    }
  }
}
